package org.example.seata.bs.order.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import com.rabbitmq.client.Channel;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.seata.bs.api.constant.order.OrderStatusEnum;
import org.example.seata.bs.api.dto.account.AccountBalanceLogDto;
import org.example.seata.bs.api.entity.order.Order;
import org.example.seata.bs.api.entity.order.OrderPayInfo;
import org.example.seata.bs.api.feign.account.RemoteAccountService;
import org.example.seata.bs.api.service.order.impl.OrderPayInfoServiceImpl;
import org.example.seata.bs.api.vo.account.AccountBalanceLogVo;
import org.example.seata.bs.api.vo.order.OrderPayInfoVo;
import org.example.seata.bs.order.service.OrderEnhanceService;
import org.example.seata.bs.order.service.OrderPayInfoEnhanceService;
import org.example.seata.common.core.constant.LKC;
import org.example.seata.common.core.util.ApiResp;
import org.example.seata.common.core.util.BaseException;
import org.example.seata.common.core.util.BusOp;
import org.example.seata.common.rabbitmq.constant.ExchangeQueueRoutingKey;
import org.example.seata.common.rabbitmq.util.MqUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Optional;

/**
 * @author hzq
 * @date 2021/11/22 17:34
 */
@Slf4j
@Service
@Primary
@DS("order")
@AllArgsConstructor
public class OrderPayInfoEnhanceServiceImpl extends OrderPayInfoServiceImpl implements OrderPayInfoEnhanceService {

    private final LockTemplate lockTemplate;
    private final RabbitTemplate rabbitTemplate;
    private final OrderEnhanceService orderEnhanceService;
    private final RemoteAccountService remoteAccountService;

    @Override
    @GlobalTransactional(name = "orderPay")
    public ApiResp<OrderPayInfoVo> orderPay(Integer orderId) {

        // 订单详情
        Order order = orderEnhanceService.getById(orderId);

        BusOp.mustNotNull(order, "订单详情不能为空");

        // 检验订单
        checkOrderPay(order);

        // 保存支付信息
        OrderPayInfo orderPayInfo = createOrderPayInfo(order);

        BusOp.mustSucceed(save(orderPayInfo), "保存支付信息失败");

        // 减去用户余额
        BusOp.mustSucceed(minusAccountBalance(orderPayInfo), "减去用户余额失败");

        // 发送支付信息到 Mq
        sendOrderPayInfoMessageToMq(orderPayInfo);

        OrderPayInfoVo vo = new OrderPayInfoVo();
        BeanUtils.copyProperties(orderPayInfo, vo);

        return ApiResp.ofSuccess(vo);
    }

    /**
     * 监听支付信息
     *
     * @param message 支付信息
     * @param channel 信道
     */
    @GlobalTransactional(name = "cancelOrderPay")
    @RabbitListener(queues = ExchangeQueueRoutingKey.ORDER_PAY_DIRECT_QUEUE)
    public void receiveOrderPayInfoMessage(Message message, Channel channel) {
        OrderPayInfo mess = MqUtil.parseMessage(message, OrderPayInfo.class);
        Optional.ofNullable(mess.getOrderPayInfoId()).ifPresent(orderPayInfoId -> {

            // todo 理论上这里应该通过订单号第三方支付平台查询一下是否支付成功

            LockInfo lock = lockTemplate.lock(LKC.OrderPayInfo.CALLBACK_TIME.getLockKey(orderPayInfoId));
            try {
                OrderPayInfo info = getById(orderPayInfoId);
                boolean notExistInfo = null == info;
                if (notExistInfo || null != info.getCallbackTime()) {
                    log.warn("支付单号【{}】{}", orderPayInfoId, notExistInfo ? "不存在" : "已经处理了、请勿重复处理");
                    return;
                }
                info.setCallbackTime(LocalDateTime.now());
                boolean payStatus = 1 == mess.getPayStatus();
                log.info("支付单号【{}】支付{}", orderPayInfoId, payStatus ? "成功" : "失败");
                // 支付成功
                if (payStatus) {
                    info.setPayStatus(1).setPayTime(info.getCallbackTime());
                    // 修改订单状态为支付成功状态
                    orderEnhanceService.updateToOrderPaySuccess(info.getOrderId());
                }
                // 修改支付状态
                updateById(info);
            } finally {
                lockTemplate.releaseLock(lock);
            }
        });
        // 手动确认
        MqUtil.basicAck(message, channel);
    }

    /**
     * 减用户余额
     *
     * @param info 支付信息
     * @return 操作结果
     */
    boolean minusAccountBalance(OrderPayInfo info) {

        AccountBalanceLogDto dto = new AccountBalanceLogDto();

        dto.setLogBalance(info.getPayTotalPrice().negate()).setDescription("购买商品");

        // 远程减用户余额
        ApiResp<AccountBalanceLogVo> resp = remoteAccountService.minusAccountBalance(dto);

        if (resp.isErrorOrIsNullData()) {
            return false;
        }

        if (resp.getData().getOperateResult()) {
            info.setPayStatus(1);
        }

        return resp.getData().getOperateResult();
    }

    /**
     * 发送支付信息到 Mq
     *
     * @param orderPayInfo 支付信息
     */
    void sendOrderPayInfoMessageToMq(OrderPayInfo orderPayInfo) {
        OrderPayInfo message = new OrderPayInfo().setOrderPayInfoId(orderPayInfo.getOrderPayInfoId()).setPayStatus(orderPayInfo.getPayStatus());
        MqUtil.sendMessageToRabbitMq(rabbitTemplate,
                ExchangeQueueRoutingKey.ORDER_DIRECT_EXCHANGE,
                ExchangeQueueRoutingKey.ORDER_PAY_DIRECT_ROUTING_KEY,
                message);
    }

    /**
     * 创建支付信息
     */
    OrderPayInfo createOrderPayInfo(Order order) {

        OrderPayInfo orderPayInfo = new OrderPayInfo();

        orderPayInfo.setPayStatus(0).setOrderId(order.getOrderId()).setPayTotalPrice(order.getOrderTotalPrice());

        return orderPayInfo;
    }

    /**
     * 校验订单支付
     *
     * @param order 订单信息
     */
    void checkOrderPay(Order order) {

        if (null == order) {
            throw new BaseException("订单信息有误");
        }

        // 只支持状态为 创建订单 --> 待支付 的订单
        if (!order.getOrderStatus().equals(OrderStatusEnum.CREATE_ORDER.getStatus())) {
            throw new BaseException("只能支付待支付订单");
        }
    }

}
